#include "hw2_syscalls.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/resource.h>


/*
 * The method gets an integer which present the value of a switching result and
 * returns a string value of the given reason.
 */
char* reasonToString(int switch_reason){
	char * stringVal;
	switch(switch_reason){
		case 1: stringVal="A task was created";
				break;
		case 2: stringVal="A task ended.";
				break;
		case 3: stringVal="A task yields the CPU.";
				break;
		case 4: stringVal="The previous task goes out for waiting.";
				break;
		case 5: stringVal="A task with higher priority returns from waiting.";
				break;
		case 6: stringVal="The time slice of the previous task has ended.";
				break;
		default: stringVal="Reason is invalid!!";
	}
	return stringVal;
}
/*
 * The method gets an array of switching and an integer which present the number
 * of switches which accrue most recently and prints the given data.
 */
void print(struct switch_info* switch_info1, int res){
	int i;
	char * string_is_new_epoch;
	printf("get_scheduling_statistic:\n");
	printf("The number of elements that were filled is: %d \n", res);
	for(i=0; i<res; i++){
		string_is_new_epoch=switch_info1[i].is_new_epoch ? "Yes": "No";
		/*printf("Previous pid:%d: Next pid:%d Previous priority:%d Next priority:%d Best priority:%d is new epoch:%s Time:%d Reason:%s \n",	\
				switch_info1[i].previous_pid, switch_info1[i].next_pid, switch_info1[i].previous_priority, switch_info1[i].next_priority, switch_info1[i].best_priority, \
				string_is_new_epoch, switch_info1[i].time, reasonToString(switch_info1[i].reason));*/
		
		printf("%5d %5d %3d %3d %3d %3s %8d %s\n",
				switch_info1[i].previous_pid, 
				switch_info1[i].next_pid, 
				switch_info1[i].previous_priority, 
				switch_info1[i].next_priority, 
				switch_info1[i].best_priority, 
				string_is_new_epoch, 
				switch_info1[i].time, 
				reasonToString(switch_info1[i].reason));
	}
	printf("\n");
}

int fibonaci(int n){
	if (n < 2)
		return n;
	return fibonaci(n-1) + fibonaci(n-2);
}

int createProccess(int n, int nice) {
	int pid = fork();
	
	if (pid == 0) {
		fibonaci(n);
		exit(0);
	}
	else if (pid > 0) {
		/* Update proccess nice */
		setpriority(PRIO_PROCESS, pid, nice);
	}
	return pid;
}

/*
 * The method gets an integer which present a given priority and returns true
 * if the given priority is valid and false otherwise.
 */
int testCheckPriority(int p){
	if ((p >100) || (p < 1)){
		return false;
	}
	return true;
}

int main(int argc, char** argv){
	/*
	 * res1- contains the result value while setting a mechanism.
	 * res2- contains the number of reason while switching.
	 * params- a struct which contains the given priorities.
	 * last_switches- an array of switches which is recieved by using the
	 * 					method get_scheduling_statistic.
	 * status- the result given by the method wait()
	 * p_prio1, p_epoch1, p_interactive1-
	 * 							the priority given by the user.
	 */
	int res1, res2, i, fork_result, n, status, p_prio1, p_epoch1, p_interactive1;
	int num, nice, pid;
	struct random_params params;
	struct switch_info last_switches[150];
	
	/* checking is input valid */
	if(argc < 6 || argc % 2 != 0){
		fprintf(stderr, "Invalid input - the input should be in the format: "
						"sched_tester <p_prio> <p_epoch> <p_interactive> <nice1> <n1> ...\n");
		return -1;
	}
	
	p_prio1 = atoi(argv[1]);
	p_epoch1 = atoi(argv[2]);
	p_interactive1 = atoi(argv[3]);
	
	/* checking if the given priorities are valid */
	if(!testCheckPriority(p_prio1) || !testCheckPriority(p_epoch1) || !testCheckPriority(p_interactive1)) {
		fprintf(stderr, "invalid priority value!\n");
		return -1;
	}
	
	params.p_prio = p_prio1;
	params.p_epoch = p_epoch1;
	params.p_interactive = p_interactive1;
	
	/*changing the mechanism to SCHED_RANDOM*/
	res1 = sched_setmechanism(MODE_REGULAR, &params);
	if (res1 == -1){
		perror("method sched_setmechanism random() failed");
		exit(-1);
	}
	
	for(i=4; i<argc; i+=2){
		num = atoi(argv[i]);
		nice = atoi(argv[i + 1]);
		pid = createProccess(num, nice);		
		if (pid < 0) {
			perror("fork()");
			exit(-1);
		}

		printf("Create proccess %d; n = %d; nice = %d\n", pid, num, nice);
	}
	printf("\n");
	
	//waiting for all tasks to be done
	while (wait(&status) != -1);

	res2 = get_scheduling_statistic(last_switches);
	if (res2 == -1) {
		perror("method get_scheduling_statistic() failed");
		exit(-1);
	}
	print(last_switches, res2);

	sched_setmechanism(MODE_REGULAR, NULL);
	return 0;
}
